<?php

declare(strict_types=1);

namespace app\admin\service;

use app\admin\model\Serialnumber as SerialnumberModel;
use app\admin\common\ServiceResponse;
use app\admin\model\Images;
use app\common\RedisClient;
use think\facade\Db;
use think\facade\Log;
use app\common\service\Upload;
use app\admin\model\Algorithm;
use app\admin\model\Product;

class Serialnumber
{
    use ServiceResponse;
    protected $model;
    protected $redis;

    public function __construct()
    {
        $this->redis = (new RedisClient())->getClient();
        $this->model = new SerialnumberModel();
    }

    public function getList($pageIndex, $pageSize, $where, $field)
    {
        $total = $this->model->where($where)->count();
        $res = $this->model
            ->where($where)
            ->page($pageIndex, $pageSize)
            ->field($field)
            ->with(['seller', 'product', 'warehouse', 'operator'])
            ->order('create_time', 'desc')
            ->select()
            ->toArray();

        $algorithmModel = new Algorithm();
        $algorithm = $algorithmModel->getAttributes('algorithm');
        $productModel = new Product();
        $computing_unit = $productModel->getAttributes('comput_unit');

        foreach ($res as &$value) {
            // 处理算法字符串
            $algorithmNames = '';
            if (!empty($value['product_algorithm'])) {
                $algorithmIds = explode(',', $value['product_algorithm']);
                $names = [];
                foreach ($algorithmIds as $algorithmId) {
                    $algorithmId = trim($algorithmId);
                    if (isset($algorithm[$algorithmId])) {
                        $names[] = $algorithm[$algorithmId];
                    }
                }
                $algorithmNames = implode(',', $names);
            }

            // 直接创建产品信息
            $value['product_info'] = [
                $algorithmNames ? 'Algorithm:' . $algorithmNames : '',
                $value['product_hashrate'] && $value['product_computing_unit'] ? 'Hashrate:' . $value['product_hashrate'] . ($computing_unit[$value['product_computing_unit']] ?? '') : '',
                $value['product_power'] ? 'Power consumption:' . $value['product_power'] . 'W' : '',
            ];

            // 批量删除不需要的字段
            unset(
                $value['product_algorithm'],
                $value['product_hashrate'],
                $value['product_power'],
                $value['pid'],
                $value['product_computing_unit'],
                $value['seller_id'],
                $value['wid'],
                $value['creator']
            );
        }

        return $this->success(['list' => $res, 'total' => $total]);
    }

    public function getDetail($id)
    {
        $result = $this->model->where('sn_id', $id)
            ->field('sn_id,seller_id,pid,wid,status,creator,create_time')
            ->with([
                'seller',
                'product',
                'warehouse',
                'operator',
                'wh_scanlog' => function ($query) {
                    $query->with(['warehouse', 'operator'])->order('create_time', 'desc');
                }
            ])->find();

        // 检查记录是否存在
        if (!$result) {
            return $this->error(100802, '记录不存在', null); // 使用合适的错误码
        }

        $res = $result->toArray();


        $action = $this->model->getAttributesMap()['action'];

        // 处理扫描日志数据
        if (isset($res['wh_scanlog']) && is_array($res['wh_scanlog'])) {
            foreach ($res['wh_scanlog'] as &$value) {
                $value['action'] = $action[$value['action']] ?? '';
                unset($value['pid']);
                unset($value['wid']);
                unset($value['creator']);
            }
        }

        // 清理不需要的字段
        unset($res['pid']);
        unset($res['seller_id']);
        unset($res['wid']);
        unset($res['creator']);

        return $this->success($res);
    }



    public function discard($params)
    {
        $sn_id = $params['sn_id'];
        $res = $this->model->where('sn_id', $sn_id)->find();
        if (empty($res)) {
            return $this->error(100802);
        }
        if ($res->getRawStatus() != 0) {
            return $this->error(100803);
        }
        $this->model->where('sn_id', $sn_id)->update(['status' => -1]);
        return $this->success();
    }

    public function scanCode($params)
    {
        $where = ['sn_id' => $params['sn_id']];


        // 验证SN码是否存在
        $snData = $this->model->where($where)->find();
        if (!$snData) {
            return $this->error(100802);
        }

        // 验证仓库是否存在
        if (empty($this->model->table('db_warehouse')->where('id', $params['wid'])->where('is_del', 0)->find())) {
            return $this->error(100804);
        }

        $scanLog = Db::name('wh_scanlog')
            ->where([
                'sn_id' => $params['sn_id'],
                'wid' => $params['wid']
            ])->order('id', 'desc')
            ->find();

        if ($scanLog && $scanLog['action'] == $params['action']) {
            return $this->error(100807);
        }

        // 局部函数：更新SN码状态
        $updateSnStatus = function ($status, $wid = null) use ($where) {
            $updateData = ['status' => $status];
            $updateData['wid'] = $wid;
            $this->model->where($where)->update($updateData);
        };

        // 局部函数：创建扫描日志
        $createScanLog = function ($action) use ($params, $snData) {
            Db::name('wh_scanlog')->insert([
                'sn_id' => $params['sn_id'],
                'wid' => $params['wid'],
                'action' => $action,
                'pid' => $snData['pid'],
                'creator' => $params['creator'],
                'create_time' => date('Y-m-d H:i:s')
            ]);
        };



        try {
            Db::startTrans();

            switch ($params['action']) {
                case 2: // 入库 
                    $updateSnStatus(2, $params['wid']);
                    $createScanLog(2);
                    break;

                case 3: // 出库
                    $updateSnStatus(3, null);
                    $createScanLog(3);
                    break;

                case 4: // 维修
                    $updateSnStatus(4, $params['wid']);
                    $createScanLog(4);
                    break;
                default :
                    $updateSnStatus($params['action'],null);
                    $createScanLog($params['action']);
            }

            Db::commit();
            return $this->success();
        } catch (\Exception $e) {
            Db::rollback();
            return $this->error(500001);
        }
    }

    public function getFieldEnum($field = 'all')
    {
        $res = $this->model->getFieldEnum($field);
        return [
            'code' => 0,
            'data' => $res
        ];
    }

    /**
     * @describe
     * @author: Bobo
     * @date: 2025-06-09 17:44:05 
     * @param [type] @required
     * @return Renderable
     */
    public function getScanlog($where, $pageIndex, $pageSize, $field)
    {
        $res = $this->model
            ->table('db_wh_scanlog')
            ->where($where)
            ->with(['product', 'warehouse', 'operator'])
            ->order('id', 'desc')
            ->page($pageIndex, $pageSize)
            ->field($field)
            ->select()
            ->toArray();
        $action = $this->model->getAttributesMap()['action'];
        foreach ($res as &$value) {
            $value['action'] = $action[$value['action']] ?? '';
            unset($value['pid']);
            unset($value['wid']);
            unset($value['creator']);
        }
        $total = $this->model->table('db_wh_scanlog')->where($where)->count();
        return $this->success(['list' => $res, 'total' => $total]);
    }

    // 基础常量定义
    private const CANVAS_WIDTH = 227;    // 60mm = 227像素 (96dpi)
    private const CANVAS_HEIGHT = 151;   // 40mm = 151像素 (96dpi)
    private const BARCODE_WIDTH = 200;   // 条形码宽度
    private const BARCODE_HEIGHT = 114;   // 条形码高度
    private const TOP_MARGIN = 10;       // 顶部留白
    private const TEXT_MARGIN = 15;      // 文字间距
    private const TTF_FONT_SIZE = 10;    // TTF字体大小
    private const FALLBACK_TTF_SIZE = 14; // 备用TTF字体大小
    private const FALLBACK_FONT_SIZE = 4; // 内置字体大小

    /**
     * 生成序列号条形码图片
     * @param array $params 参数数组，包含sn_id
     * @return array
     */
    public function generateSnPictures($params)
    {
        $sn_id = $params['sn_id'];

        // 验证序列号并检查是否已有图片
        $snRecord = $this->validateAndGetSnRecord($sn_id);

        if (isset($snRecord['code'])) {
            return $snRecord; // 返回错误或已存在的图片URL
        }

        try {
            // 生成条形码
            $barcodeResult = $this->generateBarcode($sn_id);
            if (!$barcodeResult) {
                return $this->error(500002, '条形码生成失败，请检查序列号格式');
            }

            // 生成第一张图片：普通条形码图片 (photo)
            $canvas1 = $this->createCanvas();
            $this->drawBarcode($canvas1, $barcodeResult, $sn_id);
            $photoData = $this->saveAndUploadImage($canvas1, $sn_id, $snRecord, 'photo');

            // 生成第二张图片：带产品名称的条形码图片 (photo_on_box)
            $productName = $snRecord['product']['name'] ?? $snRecord['product_name'] ?? '';
            $canvas2 = $this->createCanvas(true); // 使用大尺寸画布
            $this->drawBarcodeWithProductName($canvas2, $barcodeResult, $sn_id, $productName);
            $photoOnBoxData = $this->saveAndUploadImage($canvas2, $sn_id, $snRecord, 'photo_on_box');

            // 更新数据库记录
            $this->updateSnRecordWithPhotos($sn_id, $photoData, $photoOnBoxData);

            return $this->success([
                'photo_url' => $photoData['url'],
                'photo_on_box_url' => $photoOnBoxData['url']
            ]);
        } catch (\Exception $e) {
            return $this->error(500001, '生成条形码失败：' . $e->getMessage());
        }
    }

    /**
     * 验证序列号并获取记录
     */
    private function validateAndGetSnRecord($sn_id)
    {
        $res = $this->model->where('sn_id', $sn_id)->with(['product'])->find();

        if (empty($res)) {
            return $this->error(100802, '序列号不存在');
        }

        // 检查是否已有图片
        if ($res['photo'] && $res['photo_on_box']) {
            $image = new Images();
            $photoRes = $image->getImageById($res['photo']);
            $photoOnBoxRes = $image->getImageById($res['photo_on_box']);

            if ($photoRes && $photoOnBoxRes) {
                return $this->success([
                    'photo_url' => $photoRes->url,
                    'photo_on_box_url' => $photoOnBoxRes->url
                ]);
            }
        }

        return $res->toArray();
    }

    /**
     * 创建画布
     */
    private function createCanvas($isBoxVersion = false)
    {
        $scale = $isBoxVersion ? 2 : 1;
        $width = self::CANVAS_WIDTH * $scale;
        $height = self::CANVAS_HEIGHT * $scale;

        $image = imagecreate($width, $height);
        $white = imagecolorallocate($image, 255, 255, 255);
        $black = imagecolorallocate($image, 0, 0, 0);
        imagefill($image, 0, 0, $white);

        return ['image' => $image, 'white' => $white, 'black' => $black, 'width' => $width, 'height' => $height, 'scale' => $scale];
    }

    /**
     * 生成条形码
     */
    private function generateBarcode($data)
    {
        try {
            // 验证数据长度和格式
            if (empty($data) || strlen($data) > 80) {
                throw new \Exception('序列号长度不符合要求（1-80字符）');
            }

            $generator = new \Picqer\Barcode\BarcodeGeneratorPNG();
            $barcodeData = $generator->getBarcode($data, $generator::TYPE_CODE_128, 2, 60);

            if (empty($barcodeData)) {
                throw new \Exception('条形码数据生成为空');
            }

            return $barcodeData;
        } catch (\Exception $e) {
            // 记录详细错误信息（可选）
            Log::error('条形码生成失败: ' . $e->getMessage() . ' - 数据: ' . $data);
            return false;
        }
    }

    /**
     * 绘制条形码
     */
    private function drawBarcode($canvas, $barcodeData, $sn_id)
    {
        ['image' => $image] = $canvas;

        // 从二进制数据创建图像
        $barcodeImage = imagecreatefromstring($barcodeData);
        if (!$barcodeImage) {
            $this->drawTextOnly($canvas, $sn_id, self::CANVAS_HEIGHT / 2, self::FALLBACK_TTF_SIZE);
            return;
        }

        // 获取条形码图像尺寸
        $barcodeOriginalWidth = imagesx($barcodeImage);
        $barcodeOriginalHeight = imagesy($barcodeImage);

        // 计算条形码位置（居中）
        $barcodeStartX = (self::CANVAS_WIDTH - self::BARCODE_WIDTH) / 2;
        $barcodeStartY = self::TOP_MARGIN;

        // 将条形码复制到画布上并缩放
        imagecopyresampled(
            $image,
            $barcodeImage,
            (int)$barcodeStartX,
            (int)$barcodeStartY,
            0,
            0,
            self::BARCODE_WIDTH,
            self::BARCODE_HEIGHT,
            $barcodeOriginalWidth,
            $barcodeOriginalHeight
        );

        // 释放条形码图像资源
        imagedestroy($barcodeImage);

        // 添加文字
        $textY = $barcodeStartY + self::BARCODE_HEIGHT + self::TEXT_MARGIN;
        $this->drawTextOnly($canvas, $sn_id, $textY, self::TTF_FONT_SIZE);
    }

    /**
     * 绘制带产品名称的条形码（包装盒版本，2倍尺寸）
     */
    private function drawBarcodeWithProductName($canvas, $barcodeData, $sn_id, $productName)
    {
        ['image' => $image, 'width' => $canvasWidth, 'height' => $canvasHeight, 'scale' => $scale] = $canvas;

        $currentY = self::TOP_MARGIN * $scale; // 按比例缩放的上边距
        $productNameHeight = 0; // 记录产品名称占用的高度

        // 绘制产品名称（支持换行）
        if (!empty($productName)) {
            $startY = $currentY;
            $fontSize = self::TTF_FONT_SIZE;
            $currentY = $this->drawMultiLineText($canvas, $productName, $currentY, $fontSize);
            $productNameHeight = $currentY - $startY; // 计算产品名称占用的高度

        }

        // 从二进制数据创建图像
        $barcodeImage = imagecreatefromstring($barcodeData);
        if (!$barcodeImage) {
            $this->drawTextOnly($canvas, $sn_id, $canvasHeight / 2, self::FALLBACK_TTF_SIZE);
            return;
        }

        // 获取条形码图像尺寸
        $barcodeOriginalWidth = imagesx($barcodeImage);
        $barcodeOriginalHeight = imagesy($barcodeImage);

        // 计算条形码位置（居中）
        $barcodeWidth = self::BARCODE_WIDTH * $scale;
        // 条形码高度：基础高度*缩放比例，如果有产品名称则减去占用的高度
        $barcodeHeight = self::BARCODE_HEIGHT * $scale;
        if (!empty($productName)) {
            // 减去产品名称占用的高度，保持条形码在合理范围内
            $barcodeHeight = $barcodeHeight - ($productNameHeight); // 适当减少高度
        }
        $barcodeStartX = ($canvasWidth - $barcodeWidth) / 2;

        $barcodeStartY = $currentY;

        // 将条形码复制到画布上并缩放
        imagecopyresampled(
            $image,
            $barcodeImage,
            (int)$barcodeStartX,
            (int)$barcodeStartY,
            0,
            0,
            (int)$barcodeWidth,
            (int)$barcodeHeight,
            $barcodeOriginalWidth,
            $barcodeOriginalHeight
        );

        // 释放条形码图像资源
        imagedestroy($barcodeImage);

        // 添加序列号文字
        $textY = $barcodeStartY + $barcodeHeight + self::TEXT_MARGIN * $scale;
        $fontSize = self::TTF_FONT_SIZE;
        $this->drawSingleLineText($canvas, $sn_id, $textY, $fontSize);
    }

    /**
     * 绘制文字
     */
    private function drawTextOnly($canvas, $text, $yPosition, $fontSize)
    {
        ['image' => $image, 'black' => $black] = $canvas;

        $fontPath = $this->getFontPath();

        if ($fontPath && file_exists($fontPath)) {
            $this->drawTtfText($image, $text, $fontPath, $fontSize, $yPosition, $black);
        } else {
            $this->drawBuiltinText($image, $text, self::FALLBACK_FONT_SIZE, $yPosition, $black);
        }
    }

    /**
     * 获取字体路径
     */
    private function getFontPath()
    {
        $fontPaths = [
            app()->getRootPath() . 'public/static/fonts/SourceHanSansCN-Regular.ttf', // 思源黑体（支持中文）
            app()->getRootPath() . 'public/static/fonts/simhei.ttf',                // 黑体（支持中文）
            app()->getRootPath() . 'public/static/fonts/WenQuanYiMicroHei.ttf',     // 文泉驿微米黑（支持中文）
            app()->getRootPath() . 'public/static/fonts/arial.ttf',                  // Arial（英文）
            app()->getRootPath() . 'public/static/fonts/helvetica.ttf'               // Helvetica（英文）
        ];

        foreach ($fontPaths as $path) {
            if (file_exists($path)) {
                return $path;
            }
        }

        return null;
    }

    /**
     * 绘制TTF字体文字
     */
    private function drawTtfText($image, $text, $fontPath, $fontSize, $yPosition, $color)
    {
        $textBox = imagettfbbox($fontSize, 0, $fontPath, $text);
        $textWidth = $textBox[4] - $textBox[0];
        $textX = (self::CANVAS_WIDTH - $textWidth) / 2;

        imagettftext($image, $fontSize, 0, (int)$textX, (int)$yPosition, $color, $fontPath, $text);
    }

    /**
     * 绘制内置字体文字
     */
    private function drawBuiltinText($image, $text, $fontSize, $yPosition, $color)
    {
        $textWidth = strlen($text) * imagefontwidth($fontSize);
        $textX = (self::CANVAS_WIDTH - $textWidth) / 2;

        imagestring($image, $fontSize, (int)$textX, (int)$yPosition, $text, $color);
    }

    /**
     * 绘制多行文字（支持自动换行）
     */
    private function drawMultiLineText($canvas, $text, $startY, $fontSize)
    {
        ['image' => $image, 'black' => $black, 'width' => $canvasWidth] = $canvas;

        $fontPath = $this->getFontPath();
        $lineHeight = $fontSize + 8; // 行高
        $maxWidth = $canvasWidth - 40; // 预留左右边距
        $currentY = $startY;


        if ($fontPath && file_exists($fontPath)) {
            // 使用TTF字体
            $lines = $this->wrapText($text, $fontPath, $fontSize, $maxWidth);

            foreach ($lines as $line) {
                $textBox = imagettfbbox($fontSize, 0, $fontPath, $line);
                $textWidth = $textBox[4] - $textBox[0];
                $textX = ($canvasWidth - $textWidth) / 2;

                imagettftext($image, $fontSize, 0, (int)$textX, (int)$currentY, $black, $fontPath, $line);
                $currentY += $lineHeight;
            }
        } else {
            // 使用内置字体
            $lines = $this->wrapTextBuiltin($text, $maxWidth, self::FALLBACK_FONT_SIZE);

            foreach ($lines as $line) {
                $textWidth = strlen($line) * imagefontwidth(self::FALLBACK_FONT_SIZE);
                $textX = ($canvasWidth - $textWidth) / 2;

                imagestring($image, self::FALLBACK_FONT_SIZE, (int)$textX, (int)$currentY, $line, $black);
                $currentY += $lineHeight;
            }
        }

        return $currentY;
    }

    /**
     * 绘制单行文字（包装盒版本）
     */
    private function drawSingleLineText($canvas, $text, $yPosition, $fontSize)
    {
        ['image' => $image, 'black' => $black, 'width' => $canvasWidth] = $canvas;

        $fontPath = $this->getFontPath();

        if ($fontPath && file_exists($fontPath)) {
            $textBox = imagettfbbox($fontSize, 0, $fontPath, $text);
            $textWidth = $textBox[4] - $textBox[0];
            $textX = ($canvasWidth - $textWidth) / 2;

            imagettftext($image, $fontSize, 0, (int)$textX, (int)$yPosition, $black, $fontPath, $text);
        } else {
            $textWidth = strlen($text) * imagefontwidth(self::FALLBACK_FONT_SIZE);
            $textX = ($canvasWidth - $textWidth) / 2;

            imagestring($image, self::FALLBACK_FONT_SIZE, (int)$textX, (int)$yPosition, $text, $black);
        }
    }

    /**
     * TTF字体文字换行处理
     */
    private function wrapText($text, $fontPath, $fontSize, $maxWidth)
    {
        $words = explode(' ', $text);
        $lines = [];
        $currentLine = '';

        foreach ($words as $word) {
            $testLine = empty($currentLine) ? $word : $currentLine . ' ' . $word;
            $textBox = imagettfbbox($fontSize, 0, $fontPath, $testLine);
            $textWidth = $textBox[4] - $textBox[0];

            if ($textWidth <= $maxWidth) {
                $currentLine = $testLine;
            } else {
                if (!empty($currentLine)) {
                    $lines[] = $currentLine;
                    $currentLine = $word;
                } else {
                    // 单词太长，强制换行
                    $lines[] = $word;
                }
            }
        }

        if (!empty($currentLine)) {
            $lines[] = $currentLine;
        }

        return $lines;
    }

    /**
     * 内置字体文字换行处理
     */
    private function wrapTextBuiltin($text, $maxWidth, $fontSize)
    {
        $charWidth = imagefontwidth($fontSize);
        $maxChars = intval($maxWidth / $charWidth);

        return str_split($text, $maxChars);
    }

    /**
     * 保存并上传图片
     */
    private function saveAndUploadImage($canvas, $sn_id, $snRecord, $type = 'photo')
    {
        ['image' => $image] = $canvas;

        // 创建临时文件
        $uploadPath = root_path() . 'public/static/uploads/temp/';
        if (!is_dir($uploadPath)) {
            mkdir($uploadPath, 0755, true);
        }

        $filename = 'barcode_' . $type . '_' . $sn_id . '_' . time() . '_' . mt_rand(1000, 9999) . '.png';
        $filepath = $uploadPath . $filename;

        // 保存图像
        $saved = imagepng($image, $filepath);
        imagedestroy($image);

        if (!$saved) {
            throw new \Exception('图片保存失败');
        }

        // 上传到OSS
        $upload = new Upload();
        $file = new \think\File($filepath);
        $uploadResult = $upload->uploadImages($file, 15);

        if (!$uploadResult || $uploadResult['code'] !== 0) {
            unlink($filepath);
            throw new \Exception('图片上传失败');
        }

        // 清理临时文件
        unlink($filepath);

        return [
            'url' => $uploadResult['data']['url'],
            'id' => $uploadResult['data']['id']
        ];
    }

    /**
     * 更新序列号记录的图片信息
     */
    private function updateSnRecordWithPhotos($sn_id, $photoData, $photoOnBoxData)
    {
        $this->model->where('sn_id', $sn_id)->update([
            'photo' => $photoData['id'],
            'photo_on_box' => $photoOnBoxData['id']
        ]);
    }

    public function save($params)
    {
        if (empty($this->model->table('db_product')->where('id', $params['pid'])->where('is_del', 0)->find())) {
            return $this->error(100800);
        }



        // 批量生成SN码，使用分布式锁确保并发安全
        $sn_ids = $this->createSnIds($params['seller_id'], $params['pid'], $params['number']);

        if (empty($sn_ids)) {
            return $this->error(100801, '生成SN码失败');
        }

        // 批量插入数据库
        $insert_data = [];
        $create_time = date('Y-m-d H:i:s');
        foreach ($sn_ids as $sn_id) {
            $insert_data[] = [
                'sn_id' => $sn_id,
                'seller_id' => $params['seller_id'],
                'pid' => $params['pid'],
                'creator' => $params['creator'],
                'create_time' => $create_time
            ];
        }

        $this->model->insertAll($insert_data);

        $productModel = new Product();
        $product = $productModel->where('id', $params['pid'])->find();
        if (!empty($product['algorithm'])) {
            $algorithmModel = new Algorithm();
            $algorithm = $algorithmModel->getAttributes('algorithm');
            $algorithmNames = '';
            $algorithmIds = explode(',', $product['algorithm']);
            $names = [];
            foreach ($algorithmIds as $algorithmId) {
                $algorithmId = trim($algorithmId);
                if (isset($algorithm[$algorithmId])) {
                    $names[] = $algorithm[$algorithmId];
                }
            }
            $algorithmNames = implode(',', $names);
        }
        if (!empty($product['hashrate'])) {
            $productModel = new Product();
            $computing_unit = $productModel->getAttributes('comput_unit');
            $product_hashrate = $product['hashrate'] . ($computing_unit[$product['computing_unit']] ?? '');
        }
        $product_info = [
            $algorithmNames ? 'Algorithm:' . $algorithmNames : '',
            $product_hashrate ? 'Hashrate:' . $product_hashrate : '',
            $product['power'] ? 'Power consumption:' . $product['power'] . 'W' : '',
        ];


        return $this->success([
            'pid' => $params['pid'],
            'sn_ids' => $sn_ids,
            'number' => $params['number'],
            'product_info' => $product_info
        ]);
    }

    /**
     * 批量生成连续的SN码
     * @param int $seller_id 供应商ID
     * @param int $pid 产品ID
     * @param int $number 生成数量
     * @return array 生成的SN码数组
     */
    public function createSnIds($seller_id, $pid, $number)
    {
        $year_month = date('ym');
        $lock_key = 'sn_lock:' . $year_month;
        $counter_key = 'sn_counter:' . $year_month;

        // 设置分布式锁，超时时间10秒
        $lock_value = uniqid();
        $lock_timeout = 10;

        // 尝试获取锁 - 使用Predis兼容的语法
        $is_locked = $this->redis->set($lock_key, $lock_value, 'EX', $lock_timeout, 'NX');

        if (!$is_locked) {
            // 获取锁失败，等待重试
            usleep(100000); // 等待100毫秒
            $is_locked = $this->redis->set($lock_key, $lock_value, 'EX', $lock_timeout, 'NX');
            if (!$is_locked) {
                throw new \Exception('操作失败，请稍后重试');
            }
        }

        try {
            // 获取当前计数器值
            $current_counter = $this->redis->get($counter_key) ?: 0;

            // 检查是否超出每月上限
            if ($current_counter + $number > 16777216) {
                throw new \Exception('当月SN码生成数量将超出上限');
            }

            // 批量递增计数器
            $start_counter = $this->redis->incrby($counter_key, $number);
            $start_counter = $start_counter - $number + 1; // 获取起始计数器值

            // 如果是当月第一次生成，设置过期时间
            if ($start_counter == 1) {
                $expire_timestamp = 3600 * 24 * 32;
                $this->redis->expire($counter_key, $expire_timestamp);
            }

            // 生成SN码数组
            $sn_ids = [];
            for ($i = 0; $i < $number; $i++) {
                $counter = $start_counter + $i;
                $sn_id = $this->generateSingleSn($seller_id, $pid, $year_month, $counter);
                $sn_ids[] = $sn_id;
            }

            return $sn_ids;
        } finally {
            // 释放锁 - 使用Lua脚本安全释放
            $script = "
                if redis.call('get', KEYS[1]) == ARGV[1] then
                    return redis.call('del', KEYS[1])
                else
                    return 0
                end
            ";
            $this->redis->eval($script, 1, $lock_key, $lock_value);
        }
    }

    /**
     * 生成单个SN码
     * @param int $seller_id 供应商ID
     * @param int $pid 产品ID
     * @param string $year_month 年月标识
     * @param int $counter 计数器值
     * @return string SN码
     */
    private function generateSingleSn($seller_id, $pid, $year_month, $counter)
    {
        // 1-2位：固定标识 JM
        $sn = 'JM';

        // 3-6位：产品型号标识，由SKU商品的id转成16进制，长度4位，不足前面补0
        $product_hex = strtoupper(sprintf('%04X', $pid));
        $sn .= $product_hex;

        // 7-8位：供应商标识，由供应商id转成16进制，长度2位，不足前面补0
        $seller_hex = strtoupper(sprintf('%02X', $seller_id));
        $sn .= $seller_hex;

        // 9-12位：时间标识，年份后两位+月份，长度4位
        $sn .= $year_month;

        // 13-18位：当月唯一编码，16进制字符且字母大写，长度6位
        $unique_hex = strtoupper(sprintf('%06X', $counter));
        $sn .= $unique_hex;

        return $sn;
    }

    public function createSnId($seller_id, $pid)
    {
        // 兼容原有单个生成方法，调用批量生成方法
        $sn_ids = $this->createSnIds($seller_id, $pid, 1);
        return $sn_ids[0] ?? '';
    }
}
